﻿using System.Reflection;

namespace Foundation.Core
{
    public class SelectItemLoadMethodOptions
    {
        private bool? _isNoReferenceMethod;
        private bool? _parametersAllOptional;

        public SelectItemLoadMethodOptions(MethodInfo methodInfo, SelectItemsMethodAttribute attr)
        {
            Method = methodInfo;
            IsAsync = Method.ReturnType.IsGenericType && Method.ReturnType.GetGenericTypeDefinition() == typeof(Task<>);
            Cacheable = attr.CacheMinutes > 0;

            var parameters = Method.GetParameters();

            if (parameters.Length > 0)
            {
                var pOptons = new List<SelectItemLoadMethodParameter>();

                foreach (var p in parameters)
                {
                    pOptons.Add(new SelectItemLoadMethodParameter(p));
                }

                Parameters = pOptons;
            }
        }

        public MethodInfo Method { get; protected set; }

        public IList<SelectItemLoadMethodParameter> Parameters { get; protected set; }

        /// <summary>
        /// 是否可以缓存结果
        /// </summary>
        public bool Cacheable { get; set; }

        /// <summary>
        /// 是否无参方法：没有输入参数或者只有一个 <see cref="CancellationToken"/> 输入参数
        /// </summary>
        public bool IsNoReferenceMethod
        {
            get
            {
                if (!_isNoReferenceMethod.HasValue)
                {
                    _isNoReferenceMethod = Parameters == null || Parameters.Count == 0 || Parameters[0].IsCancellationToken;
                }

                return _isNoReferenceMethod.Value;
            }
        }

        /// <summary>
        /// 是否都是可选参数
        /// </summary>
        public bool ParametersAllOptional
        {
            get
            {
                if (!_parametersAllOptional.HasValue)
                {
                    _parametersAllOptional = Parameters.All(a => a.IsOptional || a.IsCancellationToken);
                }

                return _parametersAllOptional.Value;
            }
        }

        /// <summary>
        /// 是否异步方法
        /// </summary>
        public bool IsAsync { get; protected set; }

        public Type ParamaterConverterTypeFactory { get; set; }

        public ISelectItemLoadMethodConverter ParamaterConverter { get; set; }

        public bool HasScanParameterValueProvider { get; set; }
    }

    public class SelectItemLoadMethodParameter
    {
        private bool _isNullable;
        private Type _parameterUnderlyingType;
        private bool? _isCancellationToken;
        private bool? _isSimpleType;

        private bool? _isListType;
        private bool? _isSimpleListType;
        private Type _itemType;
        private bool? _itemTypeIsNullable;
        private Type _itemUnderlyingType;

        public SelectItemLoadMethodParameter(ParameterInfo parameterInfo)
        {
            ParameterInfo = parameterInfo;

            _isNullable = parameterInfo.ParameterType.IsNullableType();
            _parameterUnderlyingType = Nullable.GetUnderlyingType(parameterInfo.ParameterType) ?? parameterInfo.ParameterType;

            var attr = parameterInfo.GetCustomAttribute<SelectItemsParameterAttribute>();
            if (attr != null)
            {
                this.ValueProviderType = attr.ValueProvider;
            }
        }

        /// <summary>
        /// 参数信息
        /// </summary>
        public ParameterInfo ParameterInfo { get; protected set; }

        /// <summary>
        /// 参数名
        /// </summary>
        public string PrameterName => ParameterInfo.Name;

        /// <summary>
        /// 参数类型
        /// </summary>
        public Type ParameterType => ParameterInfo.ParameterType;

        public bool IsNullable => _isNullable;

        public Type ParameterUnderlyingType => _parameterUnderlyingType;

        /// <summary>
        /// 是否有默认值
        /// </summary>
        public bool HasDefaultValue => ParameterInfo.HasDefaultValue;

        /// <summary>
        /// 默认值
        /// </summary>
        public object DefaultValue => ParameterInfo.DefaultValue;

        /// <summary>
        /// 是否可选参数
        /// </summary>
        public bool IsOptional => ParameterInfo.IsOptional;

        public bool IsCancellationToken
        {
            get
            {
                if (!_isCancellationToken.HasValue)
                {
                    _isCancellationToken = ParameterType == typeof(CancellationToken);
                }

                return _isCancellationToken.Value;
            }
        }

        /// <summary>
        /// 是否为简单类型：bool、byte、sbyte、short、ushort、int、uint、long、ulong、IntPtr、UIntPtr、char、double、string、enum、datetime
        ///  以及对应的可空类型
        /// </summary>
        public bool IsSimpleType
        {
            get
            {
                if (!_isSimpleType.HasValue)
                {
                    _isSimpleType = IsAllowPrimitiveType(ParameterType);

                }

                return _isSimpleType.Value;
            }
        }

        public bool IsListType
        {
            get
            {
                if (!_isListType.HasValue)
                {
                    _isListType = IsEnumerableType(ParameterUnderlyingType, out var itemType) && itemType != null;

                    if (_isListType.Value)
                    {
                        _itemType = itemType;
                    }
                }

                return _isListType.Value;
            }
        }

        /// <summary>
        /// 是否是简单类型的集合
        /// </summary>
        public bool IsSimpleListType
        {
            get
            {
                if (!_isSimpleListType.HasValue)
                {
                    var flag = false;

                    if (IsListType)
                    {
                        var t = ParameterType;
                        Type listType = null;
                        flag = t.IsArray || (t.IsGenericType && (listType = t.GetGenericTypeDefinition()) == typeof(IList<>) || listType == typeof(List<>) || listType == typeof(IEnumerable<>));

                        if (flag)
                        {
                            flag = IsAllowPrimitiveType(ItemType);
                        }
                    }

                    _isSimpleListType = flag;
                }

                return _isSimpleListType.Value;
            }
        }

        public Type ItemType
        {
            get
            {
                if (IsListType && _itemType == null)
                {
                    if (IsEnumerableType(ParameterType, out var itemType))
                    {
                        _itemType = itemType;
                    }
                }

                return _itemType;
            }
        }

        public bool ItemTypeIsNullable
        {
            get
            {
                if (!_itemTypeIsNullable.HasValue)
                {
                    var itmType = ItemType;
                    var flag = false;
                    if (itmType != null)
                    {
                        flag = itmType.IsNullableType();
                    }
                    _itemTypeIsNullable = flag;
                }

                return _itemTypeIsNullable.Value;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public Type ItemUnderlyingType
        {
            get
            {
                if (IsListType && _itemUnderlyingType == null)
                {
                    var itemType = ItemType;
                    _itemUnderlyingType = Nullable.GetUnderlyingType(itemType) ?? itemType;
                }

                return _itemUnderlyingType;
            }
        }

        public bool HasSetValueFactory { get; set; }

        public Func<string, object> ValueFactory { get; set; }

        public Type ValueProviderType { get; set; }

        public static bool IsAllowPrimitiveType(Type t)
        {
            t = Nullable.GetUnderlyingType(t) ?? t;
            return t.IsPrimitive || t.IsEnum || t == typeof(string) || t == typeof(DateTime) || t == typeof(decimal);
        }

        public static bool IsEnumerableType(Type t, out Type itemType)
        {
            itemType = default;

            if (t.IsPrimitive || t.IsEnum || t.IsValueType || t == typeof(string))
            {
                return false;
            }

            while (t != null)
            {
                if (t.IsArray)
                {
                    itemType = t.GetElementType();
                    return true;
                }

                if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IEnumerable<>))
                {
                    itemType = t.GenericTypeArguments[0];
                    return true;
                }

                foreach (var item in t.GetInterfaces())
                {
                    if (item.IsGenericType && item.GetGenericTypeDefinition() == typeof(IEnumerable<>))
                    {
                        itemType = item.GenericTypeArguments[0];
                        return true;
                    }
                }

                t = t.BaseType;
            };

            return false;
        }
    }

    public struct SelectItemLoadMethodParameterConvertResult
    {
        /// <summary>
        /// 是否转换成功
        /// </summary>
        public bool IsSuccss;

        /// <summary>
        /// 是否发现参数
        /// </summary>
        public bool IsFound;

        /// <summary>
        /// 是否支持该参数类型
        /// </summary>
        public bool IsSupport;

        /// <summary>
        /// 参数转换结果
        /// </summary>
        public object Value;
    }

    public interface ISelectItemLoadMethodConverter
    {
        string BuildCacheKey(IDictionary<string, object> argumets);

        SelectItemLoadMethodParameterConvertResult ConvertValue(IDictionary<string, object> argumets, SelectItemLoadMethodParameter parameter);
    }

    public interface ISelectItemLoadMethodConverterFactory
    {
        ISelectItemLoadMethodConverter Create(SelectItemLoadMethodOptions options);
    }

}
